/**
 * Copyright (c) suzuki ryoutoku
 * See this code regarding the Apache License Version 2.0
 * @file StripCreator.cpp
 * @author suzuki ryoutoku
 */

//=============================================================================
//	pre processor
//=============================================================================
#include "StripCreator.h"
#include "Graphics.h"

using namespace library::graphics;
using namespace strip;
using namespace library::log;

//=============================================================================
//	strip::Edge's method
//=============================================================================
/**
 * @author suzuki ryoutoku
 * @date 2007-11-2
 * @version 1.0
 */
Edge::Edge()
{
  this->m_ascending = NULL;
  this->m_descending = NULL;
}

/**
 * @author suzuki ryoutoku
 * @date 2007-11-2
 * @version 1.0
 */
Edge::~Edge()
{
}

/**
 * To search neighbour triabgle via edge
 * \note
 *
 *
 * @author suzuki ryoutoku
 * @date 2007-11-5
 * @version 1.0
 */
strip::Triangle* Edge::getNeighbourTriangle(strip::Triangle* face)
{
  if( face == this->m_ascending ){
	return this->m_descending;
  }else if( face == m_descending ){
	return this->m_ascending;
  }
  return NULL;
}

//=============================================================================
//	Triangle's method
//=============================================================================
/**
 * @author suzuki ryoutoku
 * @date 2007-11-2
 * @version 1.0
 */
Triangle::Triangle(int arg1, int arg2, int arg3)
{
  memset(this, 0, sizeof(strip::Triangle));
  this->m_index[0] = arg1;
  this->m_index[1] = arg2;
  this->m_index[2] = arg3;
  this->m_edges[0] = NULL;
  this->m_edges[1] = NULL;
  this->m_edges[2] = NULL;
}

/**
 * @author suzuki ryoutoku
 * @date 2007-11-2
 * @version 1.0
 */
Triangle::~Triangle()
{
}

/**
 * When second vertex is stored in virtual current buffer, 
 * this method search edge via which strip pass
 * \note
 * Presupposing connectivity order is clockwise.
 *
 * @param iedge previously via edge
 *
 * @return next edge via which strip pass.

 *
 * @author suzuki ryoutoku 
 * @date 2007-11-3
 * @version 1.0
 */
strip::Edge* Triangle::findViaEdgeWithCurrentBufferB(strip::Edge* iedge)
{

  int current_point = iedge->m_second;
  if( this->m_edges[0] == iedge ){
	// When strip previously passed from 0 to 1,
	// next, strip pass from 0 to 2.
	if( this->m_edge_path[0] == false ){
	  current_point = iedge->m_first;
	}
	if( this->m_edges[2]->m_first == current_point ||
		this->m_edges[2]->m_second == current_point ){
	  return this->m_edges[2];

	}else if( this->m_edges[1]->m_first == current_point ||
			  this->m_edges[1]->m_second == current_point ){
	  return this->m_edges[1];
	}
  }else if( this->m_edges[1] == iedge ){
	// When strip previously passed from 1th edge to 2th edge,
	// next, strip pass from 0th edge to 1th edge.
	if( this->m_edge_path[1] == false ){
	  current_point = iedge->m_first;
	}
	if( this->m_edges[2]->m_first == current_point ||
		this->m_edges[2]->m_second == current_point ){
	  return this->m_edges[2];

	}else if( this->m_edges[0]->m_first == current_point ||
			  this->m_edges[0]->m_second == current_point ){
	  return this->m_edges[0];
	}
  }else{
	// When strip previously passed from 2th edge to 0th edge,
	// next, strip pass from 1th edge to 2th edge.
	if( this->m_edge_path[2] == false ){
	  current_point = iedge->m_first;
	}
	if( this->m_edges[1]->m_first == current_point ||
		this->m_edges[1]->m_second == current_point ){
	  return this->m_edges[1];

	}else if( this->m_edges[0]->m_first == current_point ||
			  this->m_edges[0]->m_second == current_point ){
	  return this->m_edges[0];
	}
	
  }

}

/**
 * When first vertex is stored in virtual current buffer, 
 * this method search edge via which strip pass.
 * \note
 * Presupposing connectivity order is clockwise.
 *
 * @param iedge previously via edge
 *
 * @return next edge via which strip pass.
 *
 * @author suzuki ryoutoku 
 * @date 2007-11-3
 * @version 1.0
 */
strip::Edge* Triangle::findViaEdgeWithCurrentBufferA(strip::Edge* iedge)
{
  int current_point = iedge->m_first;
  if( this->m_edges[0] == iedge ){
	// When strip previously passed from 0th edge to 1th edge,
	// next, strip pass from 0th edge to 2th edge.
	if( this->m_edge_path[0] == false ){
	  current_point = iedge->m_second;
	}
	if( this->m_edges[2]->m_first == current_point ||
		this->m_edges[2]->m_second == current_point ){
	  return this->m_edges[2];

	}else if( this->m_edges[1]->m_first == current_point ||
			  this->m_edges[1]->m_second == current_point ){
	  return this->m_edges[1];

	}
  }else if( this->m_edges[1] == iedge ){
	// When strip previously passed from 1th edge to 2th edge,
	// next, strip pass from 0th edge to 1th edge.
	if( this->m_edge_path[1] == false ){
	  current_point = iedge->m_second;
	}
	if( this->m_edges[2]->m_first == current_point ||
		this->m_edges[2]->m_second == current_point ){
	  return this->m_edges[2];

	}else if( this->m_edges[0]->m_first == current_point ||
			  this->m_edges[0]->m_second == current_point ){
	  return this->m_edges[0];
	}
  }else{
	// When strip previously passed from 2th edge to 0th edge,
	// next, strip pass from 1th edge to 2th edge.
	if( this->m_edge_path[2] == false ){
	  current_point = iedge->m_second;
	}
	if( this->m_edges[1]->m_first == current_point ||
		this->m_edges[1]->m_second == current_point ){
	  return this->m_edges[1];

	}else if( this->m_edges[0]->m_first == current_point ||
			  this->m_edges[0]->m_second == current_point ){
	  return this->m_edges[0];
	}
	
  }
}

/**
 * To give vertex corresponding to edge.
 * \note
 *
 *
 * @author suzuki ryoutoku
 * @date 2007-11-7
 * @version 1.0
 */
int Triangle::getThitherVertex(strip::Edge* arg)
{
  if( this->m_edges[0] == arg ){
	return this->m_index[2];
  }else if( this->m_edges[1] == arg ){
	return this->m_index[0];
  }else if( this->m_edges[2] == arg ){
	return this->m_index[1];
  }
  return 0;
}

/**
 * To give vertex corresponding to triangle.
 * @author suzuki ryoutoku
 * @date 2007-11-7
 * @version 1.0
 */
int Triangle::getAcrossVertex(strip::Triangle* tri)
{
  if( tri->m_edges[0] == this->m_edges[0] ||
	  tri->m_edges[1] == this->m_edges[0] ||
	  tri->m_edges[2] == this->m_edges[0] ){
	return this->getThitherVertex(this->m_edges[0]);

  }else if( tri->m_edges[0] == this->m_edges[1] ||
			tri->m_edges[1] == this->m_edges[1] || 
			tri->m_edges[2] == this->m_edges[1] ){
	return this->getThitherVertex(this->m_edges[1]);

  }else if( tri->m_edges[0] == this->m_edges[2] || 
			tri->m_edges[1] == this->m_edges[2] || 
			tri->m_edges[2] == this->m_edges[2] ){
	return this->getThitherVertex(this->m_edges[2]);

  }
  return 0;
}

//=============================================================================
//	StripCreator's method
//=============================================================================
/**
 *
 * @author suzuki ryoutoku
 * @date 2007-10-30
 * @version 1.0
 */
StripCreator::StripCreator(library::log::LogManager* arg)
{
  this->m_logger = arg;
  char tmp[256];
  this->m_logger->print("Start time of creating strip =%s\n",
						this->m_logger->storeTime(tmp));
}

/**
 * @author suzuki ryoutoku
 * @date 2007-10-30
 * @version 1.0
 */
StripCreator::~StripCreator()
{
  m_edges.clear();
  m_triangles.clear();
  char tmp[256];
  this->m_logger->print("End time of creating strip =%s\n",
						this->m_logger->storeTime(tmp));
}

/**
 * To create strip from triangle array.
 * \note
 *
 * @param stripindex to be stored strip index
 * @param relative_triangle to be stored triangle index corresponding strip index
 * @param stripsum to be stored sum vertex of each strip
 * @param triangle connectivity of triangle
 * @param faceindex global index of triangle
 * @param triangle_sum triangle sum 
 *
 * @author suzuki ryoutoku
 * @date 2007-10-30
 * @version 1.0
 */
int StripCreator::createTriangleStrip(int*** stripindex, 
									  int*** relative_triangle,
									  int** stripsum,
									  int* triangle, 
									  int* faceindex, 
									  int triangle_sum,
									  int vertex_sum)
{
  char tmp[256];
  cout << "create triangle" << endl;
  this->createM_triangle(triangle,
						 faceindex,
						 triangle_sum);
  
  cout << "create triangle end" << endl;
  
  // create edge
  this->m_logger->print("create edge start=%s\n",
						this->m_logger->storeTime(tmp));
  this->createM_edge(vertex_sum);
  this->m_logger->print("create edge end=%s\n",
						this->m_logger->storeTime(tmp));

#if 0
  this->displayM_edge();
#endif

  /* create strip */
  vector<int*> tmp_strip_index;
  vector<int> tmp_strip_index_sum;
  vector<int*> tmp_relative_triangle;

  this->m_logger->print("triangle sum = %d\n",
						this->m_triangles.size());

  
  Triangle* start;
  // unless all triangle is converted strip index
  while( NULL != (start = this->getStartTriangle() ) ){// to determine start
	// extend to three course (order is clockwise)
	vector<strip::Triangle*> striplist1;
	vector<strip::Triangle*> striplist2;
	vector<strip::Triangle*> striplist3;
	extendNormal(striplist1,
				 start, 
				 start->m_edges[0]);
	extendNormal(striplist2,
				 start, 
				 start->m_edges[1]);
	extendNormal(striplist3,
				 start, 
				 start->m_edges[2]);
	
	// extend to three course (order is counterclockwise)
	//vector<strip::Triangle*> reverse;
	// no implement....

	// The counterclockwise is associated the clockwise
	// no implement....

	// compare strip length
	vector<strip::Triangle*> *striplist = &striplist1;
	int edge_index = 0;
	if( striplist1.size() < striplist2.size() &&
		striplist3.size() < striplist2.size() ){
	  edge_index = 1;
	  striplist = &striplist2;
	}else if( striplist1.size() < striplist3.size() &&
			  striplist2.size() < striplist3.size() ){
	  edge_index = 2;
	  striplist = &striplist3;
	}
	
	// search true start triangle

	// build triangle
	int sum = striplist->size() + 3;
	int *stripdata = new int[sum];
	int *triangleindex = new int[striplist->size() + 1];

	// stored start triangle in strip buffer
	stripdata[0] = start->getThitherVertex(start->m_edges[edge_index]);
	if( start->m_edges[edge_index]->m_ascending == start ){
	  stripdata[1] = start->m_edges[edge_index]->m_first;
	  stripdata[2] = start->m_edges[edge_index]->m_second;
	}else{
	  stripdata[2] = start->m_edges[edge_index]->m_first;
	  stripdata[1] = start->m_edges[edge_index]->m_second;
	}
	triangleindex[0] = start->m_face_index;
	int triindex = 1;
	int verindex = 3;

	vector<strip::Triangle*>::iterator tri = striplist->begin();
	Triangle* before = start;
	start->m_state = 1;
	while( tri != striplist->end() ){
	  // To get vertex corresponding to edge
	  triangleindex[triindex++] = (*tri)->m_face_index;
	  stripdata[verindex++] =  (*tri)->getAcrossVertex(before);
	  (*tri)->m_state = 1;
	  before = (*tri);
	  tri++;
	}
#if 0
	cout << "strip ";
	for( int i = 0; i < sum; i ++){
	  cout << stripdata[i] << " ";
	}
	cout << endl;
#endif
	// To store corresponding vertex in strip buffer
	tmp_strip_index.push_back(stripdata);
	tmp_strip_index_sum.push_back(sum);
	tmp_relative_triangle.push_back(triangleindex);
#if 0
	cout << sum + 3 << endl;
#endif

	striplist1.clear();
	striplist2.clear();
	striplist3.clear();
  }

  // To shift strip index from strip buffer to arguments, (stripindex,stripsum,relative_triangle)
  *stripindex = new int*[tmp_strip_index.size()];
  *relative_triangle = new int*[tmp_strip_index.size()];
  *stripsum = new int[tmp_strip_index.size()];
  for( unsigned int i = 0; i < tmp_strip_index.size(); i++ ){
	(*stripsum)[i] = tmp_strip_index_sum[i];
	(*relative_triangle)[i] = tmp_relative_triangle[i];
	(*stripindex)[i] = tmp_strip_index[i];
  }
#ifdef DEBUG
  int counter = 0;
  for( unsigned int i = 0; i < tmp_strip_index.size(); i++ ){
	counter += tmp_strip_index_sum[i];
  }
  cout << "strip list(vertex) sum = " << counter << endl;
#endif

  int retval = tmp_strip_index.size();
  tmp_strip_index.clear();
  tmp_strip_index_sum.clear();
  tmp_relative_triangle.clear();
  return retval;
}


/**
 * To store triangle infomation in working data
 * \note
 * 
 * @param triangle index array of triangle connectivity
 * @param faceindex global index of triangle
 * @param triangle_sum sum triangle
 *
 * @author suzuki ryoutoku
 * @date 2007-11-2
 * @version 1.0
 */
int StripCreator::createM_triangle(int* triangle, 
								   int* faceindex, 
								   int triangle_sum)
{
  for( int i = 0; i < triangle_sum; i ++ ){
	Triangle tri(triangle[3*i],
				 triangle[3*i+1],
				 triangle[3*i+2]);
	tri.m_face_index = faceindex[i];
	this->m_triangles.push_back(tri);
  }
  return this->m_triangles.size();
}

/**
 * To create edge infomation
 * @author suzuki ryoutoku
 * @date 2007-10-31
 * @version 1.0
 */
int StripCreator::createM_edge(int vertex_sum)
{
  int second_index[] = { 1, 2, 0};
  int edge_counter = 0;
  vector<int>	*vertex_has_line;

  vertex_has_line = new vector<int>[vertex_sum];
  
  if( this->m_triangles.size() == 0 ) return -1;



  // To repeats sum of triangle times
  for( int i = 0; i < this->m_triangles.size(); i ++ ){

	// each three edge
	for( int j = 0; j < 3; j ++){
	  Edge edge;
	  bool flag = true;
	  
	  // determine start and end of the edge
	  edge.m_first = this->m_triangles[i].m_index[j];
	  edge.m_second = this->m_triangles[i].m_index[second_index[j]];

	  // To check the edge equal aleady creating edge
	  for( int k = 0; k < vertex_has_line[edge.m_first].size(); k++){
		int edge_index = (int)(vertex_has_line[edge.m_first][k]);
		if( edge.m_first == this->m_edges[edge_index].m_second ){
		  if( edge.m_second == this->m_edges[edge_index].m_first ){
			this->m_triangles[i].m_edges[j] = (Edge*)(edge_index);
			flag = false;
			break;
		  }
		}
	  }
	  if( flag != false ){
		for( int k = 0; k < vertex_has_line[edge.m_second].size(); k++){
		  int edge_index = (int)(vertex_has_line[edge.m_second][k]);
		  if( edge.m_first == this->m_edges[edge_index].m_second ){
			if( edge.m_second == this->m_edges[edge_index].m_first ){
			  this->m_triangles[i].m_edges[j] = (Edge*)(edge_index);
			  flag = false;
			  break;
			}
		  }
		}
	  }

	  // If the edge is not made yet, insert edge array
	  if( true == flag ){
		this->m_triangles[i].m_edges[j] = (Edge*)(this->m_edges.size());
		vertex_has_line[edge.m_first].push_back(this->m_edges.size());
		vertex_has_line[edge.m_second].push_back(this->m_edges.size());
		this->m_edges.push_back(edge);
		edge_counter++;
	  }
	}
  }
  cout << "build edge end" << endl;

  cout << "relative edge" << endl;
  for( int i = 0; i < this->m_triangles.size(); i ++ ){
	for( int j = 0; j < 3; j ++){
	  int k = (int)(this->m_triangles[i].m_edges[j]);
	  if( this->m_triangles[i].m_index[j] 
		  == this->m_edges[k].m_second 
		  &&
		  this->m_triangles[i].m_index[second_index[j]]
		  == this->m_edges[k].m_first ){
		this->m_edges[k].m_descending = &(this->m_triangles[i]);
		this->m_triangles[i].m_edges[j] = &(this->m_edges[k]);
		this->m_triangles[i].m_edge_path[j] = false;
	  }else if(this->m_triangles[i].m_index[j] 
			   == this->m_edges[k].m_first 
			   &&
			   this->m_triangles[i].m_index[second_index[j]]
			   == this->m_edges[k].m_second ){
		this->m_edges[k].m_ascending = &(this->m_triangles[i]);
		this->m_triangles[i].m_edges[j] = &(this->m_edges[k]);
		this->m_triangles[i].m_edge_path[j] = true;
	  }

	}
  }


  for( int i = 0; i < vertex_sum; i ++ ){
	vertex_has_line[i].clear();
  }
  delete [] vertex_has_line;
  cout << "relative edge end" << endl;

  
  return 0;
}

/**
 * @author suzuki ryoutoku
 * @date 2007-11-2
 * @version 1.0
 */
void StripCreator::displayM_edge(void)
{
  for(int i = 0; i < this->m_edges.size(); i++){
	cout << this->m_edges[i].m_ascending <<" " << this->m_edges[i].m_descending << " ";
	cout << this->m_edges[i].m_second <<" "<< this->m_edges[i].m_first << endl;

  }
}

/**
 *
 * @author suzuki ryoutoku 
 * @date 2007-11-3
 * @version 1.0
 */
strip::Triangle* StripCreator::getStartTriangle(void)
{
  for( int i = 0; i < this->m_triangles.size(); i ++){
	if( this->m_triangles[i].m_state == 0 ){
	  return &(this->m_triangles[i]);
	}
  }
  return NULL;
}

/**
 * To extend to clockwise.
 * @author suzuki ryoutoku
 * @date 2007-11-5
 * @version 1.0
 */
bool StripCreator::extendNormal(vector<strip::Triangle*> &striplist,
								strip::Triangle* start, 
								strip::Edge* edge)
{
  strip::Triangle* now_face = start;
  strip::Triangle* next_face = NULL;
  strip::Edge* now_edge = edge;
  strip::Edge* next_edge = NULL;

  // kick-off triangle (value is false) is counterclockwise, next clockwise....
  // Turning direction is alternate counterclockwise and clockwise
  bool next_circle = false;

  if( edge == NULL ) return false;

  // unless next triangle is exist
  while( NULL != (next_face = now_edge->getNeighbourTriangle(now_face)) ){
	if( next_face == start ) break;
	// Second vertex is stored A of opengl buffer, Third is B of opengl buffer.
	// OpenGL buffer is alternate buffer A and buffer B.
	if( next_circle == false){
	  next_edge = next_face->findViaEdgeWithCurrentBufferA(now_edge);

	}else{
	  next_edge = next_face->findViaEdgeWithCurrentBufferB(now_edge);

	}
	next_circle ^= true;

	if( next_edge == NULL){
	  break;
	}
	
	// If next_face is already strip index?
	if( next_face->m_state != 0 ){
	  break;
	}

	striplist.push_back(next_face);
	now_edge = next_edge;
	now_face = next_face;
  }
  return true;
}
